package org.exhibitj.operators;

import java.util.logging.Logger;

import org.eclipse.swt.widgets.Control;
import org.exhibitj.automation.Action;
import org.exhibitj.automation.actions.ActionSequence;
import org.exhibitj.automation.actions.AgentRunner;
import org.exhibitj.automation.actions.FetchCenterPointOf;
import org.exhibitj.automation.actions.LeftMouseClickAction;
import org.exhibitj.automation.actions.MoveMouseAction;
import org.exhibitj.automation.actions.PointTracker;
import org.exhibitj.automation.actions.TypeCharacter;
import org.exhibitj.defect.Defect;

public abstract class ControlOperator {
	public static final Logger LOG = Logger.getLogger(ControlOperator.class.getSimpleName());
	
	protected final ShellOperator shell;
	
	public ControlOperator(ShellOperator shell) {
		this.shell = shell;
	}
	
	public boolean isVisible() {
		FetchingRunnable<Boolean> isVisible = new FetchingRunnable<Boolean>() {
			@Override
			public Boolean fetchValue() {
				LOG.info("Checkinf for visibility");
				return getControl().isVisible();
			}
		};
		
		return SWTDisplayThread.fetch(isVisible).booleanValue();
	}
	
	protected abstract Control getControl();
	
	public void click() {
		checkIsVisible();
		
		askAgentTo(moveMouseTo(centerOf(getControl())), and(clickLeftMouseButton()));
		
		shell.waitForEvents();
	}

	protected Action typeAsSequence(String string) {
		Action[] actions = new Action[string.length()];
		for (int i = 0; i < string.length(); i++) {
			actions[i] = new TypeCharacter(string.charAt(i));
		}
		return new ActionSequence(actions);
	}

	protected void checkIsVisible() {
		if (!isVisible()) {
			throw new NotVisibleDefect();
		}
	}

	protected <T> T and(T conjugate) {
		return conjugate;
	}

	protected void askAgentTo(Action ... actions) {
		LOG.info("requesting action");
		SWTDisplayThread.asyncExec(andPerform(actions));
	}

	protected Runnable andPerform(final Action...actions) {
		return new AgentRunner(actions);
	}

	protected Action moveMouseTo(PointTracker location) {
		return new MoveMouseAction(location);
	}

	protected Action clickLeftMouseButton() {
		return new LeftMouseClickAction();
	}

	protected PointTracker centerOf(Control control) {
		return new FetchCenterPointOf(control);
	}

	public boolean isEnabled() {
		return SWTDisplayThread.fetch(new FetchingRunnable<Boolean>() {
			@Override
			public Boolean fetchValue() {
				LOG.info("button enablement");
				return getControl().isEnabled();
			}
		});
	}

	@SuppressWarnings("serial")
	public class NotVisibleDefect extends Defect {
		public NotVisibleDefect() {
			super("The control [" + getControl() + "] is not visible, in shell [" + shell.getText() + "].");
		}
	}
}
